package com.arsn.myf.security.config;

import com.arsn.myf.security.hanlder.AuthenticationEntryPointImpl;
import com.arsn.myf.security.hanlder.LogoutHandler;
import com.arsn.myf.security.hanlder.TokenFilterHanlder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * 安全配置类
 * anyRequest             匹配所有请求路径
 * access                 SpringEl表达式结果为true时可以访问
 * anonymous              匿名可以访问
 * denyAll                用户不能访问
 * fullyAuthenticated     用户完全认证可以访问（非remember-me下自动登录）
 * hasAnyAuthority        如果有参数，参数表示权限，则其中任何一个权限可以访问
 * hasAnyRole             如果有参数，参数表示角色，则其中任何一个角色可以访问
 * hasAuthority           如果有参数，参数表示权限，则其权限可以访问
 * hasIpAddress           如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问
 * hasRole                如果有参数，参数表示角色，则其角色可以访问
 * permitAll              用户可以任意访问
 * rememberMe             允许通过remember-me登录的用户访问
 * authenticated          用户登录后可访问
 * @since 2022-06-06
 * @author zhongrj
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private HttpSecurityGreen httpSecurityGreen;

    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Autowired
    private TokenFilterHanlder tokenFilterHanlder;

    @Autowired
    private LogoutHandler logoutHandler;

    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;


    //静态资源配置
    @Override
    public void configure(WebSecurity web) throws Exception {
        //swagger2所需要用到的静态资源，允许访问
        web.ignoring().antMatchers("/swagger/**")
                .antMatchers("/swagger-ui.html")
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/v2/api-docs-ext/**")
                .antMatchers("/swagger-resources/**")
                .antMatchers("/doc.html")
                .antMatchers("/lib/**")
                .antMatchers("/login.html")
                // flowable-ui modeler
                .antMatchers("/app/**")
                .antMatchers("/i18n/**")
                .antMatchers("/views/**")
                .antMatchers("/editor-app/**")
                .antMatchers("/libs/**")
                .antMatchers("/scripts/**")
                .antMatchers("/styles/**")
                .antMatchers("/model/**")
                .antMatchers("/display/**")
                .antMatchers("/display-cmmn/**")
                .antMatchers("/images/**")
                .antMatchers("/browserconfig.xml")
                .antMatchers("/favicon.ico")
                .antMatchers("/manifest.json")
                .antMatchers("/index.html");

    }

    /**
     * HttpSecurity 自定义安全配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.可以匿名访问的url
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        //获取url,进行放行
        Arrays.asList(httpSecurityGreen.getGreen()).forEach(url->{registry.antMatchers(url).permitAll();});

        //添加跨越设置
        http.cors().configurationSource(corsConfigurationSource());
        //关闭跨域保护
        http.csrf().disable();
        //关闭自带登录框
        http.formLogin().disable();
        //2.csrf,session,token 设置
        http
            // 认证失败处理类
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            // 去除session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // 过滤请求
            .authorizeRequests()
            // 对于登录login 注册register 验证码captchaImage 允许匿名访问
            .antMatchers( "/user/login","/register", "/captchaImage").anonymous()
            // 除上面外的所有请求全部需要鉴权认证
            .anyRequest().authenticated()
            .and()
            .headers().frameOptions().disable();

        // 添加Logout filter
        http.logout().logoutUrl("/user/logout").logoutSuccessHandler(logoutHandler);
        //token 校验在前
        http.addFilterBefore(tokenFilterHanlder,UsernamePasswordAuthenticationFilter.class);
    }


    /**
     * 跨越配置
     * @return
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 设置访问源地址
        config.addAllowedOriginPattern("*");
        // 设置访问源请求头
        config.addAllowedHeader("*");
        // 设置访问源请求方法
        config.addAllowedMethod("*");
        // 有效期 1800秒
        config.setMaxAge(1800L);
        // 添加映射路径，拦截一切请求
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        // 返回新的CorsFilter
        return source;
    }

    /**
     * 定义密码加密 bean
     * @return
     */
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }


    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * 自定义认证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }

}
